package org.jeecg.modules.demo.third.member;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;

import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.Md5Util;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.demo.member.entity.JkMemberInfo;
import org.jeecg.modules.demo.member.service.IJkMemberInfoService;
import org.jeecg.modules.system.entity.SysDepart;

import org.jeecg.modules.system.model.SysLoginModel;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.util.RandImageUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;

@Tag(name="memberLoginLogout")
@RestController
@RequestMapping("/api/member")
@Slf4j
public class MemberOperationController {


    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private JeecgBaseConfig jeecgBaseConfig;
    @Autowired
    private IJkMemberInfoService sysUserService;
    @Resource
    private BaseCommonService baseCommonService;
    private final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";

    @Operation(summary = "登录接口")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @IgnoreAuth
    public Result<JSONObject> login(@RequestBody LoginRecive loginRecive, HttpServletRequest request){
        Result<JSONObject> result = new Result<JSONObject>();
        String username = loginRecive.getUsername();
        String password = loginRecive.getPassword();
        if(isLoginFailOvertimes(username)){
            return result.error500("该用户登录失败次数过多，请于10分钟后再次登录！");
        }

        // step.1 验证码check
        String captcha = loginRecive.getCaptcha();
        if(captcha==null){
            result.error500("验证码无效");
            return result;
        }
        String lowerCaseCaptcha = captcha.toLowerCase();
        // 加入密钥作为混淆，避免简单的拼接，被外部利用，用户自定义该密钥即可
        //update-begin---author:chenrui ---date:20250107  for：[QQYUN-10775]验证码可以复用 #7674------------
        String keyPrefix = Md5Util.md5Encode(loginRecive.getCheckKey()+jeecgBaseConfig.getSignatureSecret(), "utf-8");
        String realKey = keyPrefix + lowerCaseCaptcha;
        //update-end---author:chenrui ---date:20250107  for：[QQYUN-10775]验证码可以复用 #7674------------
        Object checkCode = redisUtil.get(loginRecive.getCheckKey());
        //当进入登录页时，有一定几率出现验证码错误 #1714
        if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
            log.warn("验证码错误，key= {} , Ui checkCode= {}, Redis checkCode = {}", loginRecive.getCheckKey(), lowerCaseCaptcha, checkCode);
            result.error500("验证码错误");
            // 改成特殊的code 便于前端判断
            result.setCode(HttpStatus.PRECONDITION_FAILED.value());
            return result;
        }

        // step.2 校验用户是否存在且有效
        LambdaQueryWrapper<JkMemberInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(JkMemberInfo::getUsername,username);
        JkMemberInfo memberUser = sysUserService.getOne(queryWrapper);
        result = sysUserService.checkUserIsEffective(memberUser);
        if(!result.isSuccess()) {
            return result;
        }

        // step.3 校验用户名或密码是否正确
        String userpassword = PasswordUtil.encrypt(username, password, memberUser.getSalt());
        String syspassword = memberUser.getPassword();
        if (!syspassword.equals(userpassword)) {
            addLoginFailOvertimes(username);
            result.error500("用户名或密码错误");
            return result;
        }

        // step.4  登录成功获取用户信息
        userInfo(memberUser, result, request);

        // step.5  登录成功删除验证码
        redisUtil.del(realKey);
        redisUtil.del(CommonConstantNumber.LOGIN_FAIL + username);

//        // step.6  记录用户登录日志
//        LoginUser loginUser = new LoginUser();
//        BeanUtils.copyProperties(memberUser, loginUser);
//        baseCommonService.addLog("用户名: " + username + ",登录成功！", CommonConstantNumber.LOG_TYPE_1, null,loginUser);
        return result;
    }

    /**
     * 记录登录失败次数
     * @param username
     */
    private void addLoginFailOvertimes(String username){
        String key = CommonConstantNumber.LOGIN_FAIL + username;
        Object failTime = redisUtil.get(key);
        Integer val = 0;
        if(failTime!=null){
            val = Integer.parseInt(failTime.toString());
        }
        // 10分钟，一分钟为60s
        redisUtil.set(key, ++val, 600);
    }

    private boolean isLoginFailOvertimes(String username){
        String key = CommonConstantNumber.LOGIN_FAIL + username;
        Object failTime = redisUtil.get(key);
        if(failTime!=null){
            Integer val = Integer.parseInt(failTime.toString());
            if(val>5){
                return true;
            }
        }
        return false;
    }

    /**
     * 用户信息
     *
     * @param memberUser
     * @param result
     * @return
     */
    private Result<JSONObject> userInfo(JkMemberInfo memberUser, Result<JSONObject> result, HttpServletRequest request) {
        String username = memberUser.getUsername();
        String syspassword = memberUser.getPassword();
        // 获取用户部门信息
        JSONObject obj = new JSONObject(new LinkedHashMap<>());

        //1.生成token
        String token = JwtUtil.sign(username, syspassword);
        // 设置token缓存有效时间
        redisUtil.set(CommonConstantNumber.PREFIX_USER_TOKEN + token, token);
        redisUtil.expire(CommonConstantNumber.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
        obj.put("token", token);


        //3.设置登录用户信息
        obj.put("userInfo", memberUser);


        //update-begin---author:scott ---date:2024-01-05  for：【QQYUN-7802】前端在登录时加载了两次数据字典，建议优化下，避免数据字典太多时可能产生的性能问题 #956---
        // login接口，在vue3前端下不加载字典数据，vue2下加载字典
//        String vue3Version = request.getHeader(CommonConstant.VERSION);
//        if(oConvertUtils.isEmpty(vue3Version)){
//            obj.put("sysAllDictItems", sysDictService.queryAllDictItems());
//        }
        //end-begin---author:scott ---date:2024-01-05  for：【QQYUN-7802】前端在登录时加载了两次数据字典，建议优化下，避免数据字典太多时可能产生的性能问题 #956---

        result.setResult(obj);
        result.success("登录成功");
        return result;
    }

    /**
     * 后台生成图形验证码 ：有效
     * @param response
     * @param key
     */
    @Operation(summary = "获取验证码")
    @GetMapping(value = "/randomImage/{key}")
    @IgnoreAuth
    public Result<String> randomImage(HttpServletResponse response, @PathVariable("key") String key){
        Result<String> res = new Result<String>();
        try {
            //生成验证码
            String code = RandomUtil.randomString(BASE_CHECK_CODES,4);
            //存到redis中
            String lowerCaseCode = code.toLowerCase();

            //update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
            // 加入密钥作为混淆，避免简单的拼接，被外部利用，用户自定义该密钥即可
            //update-begin---author:chenrui ---date:20250107  for：[QQYUN-10775]验证码可以复用 #7674------------
//            String keyPrefix = Md5Util.md5Encode(key+jeecgBaseConfig.getSignatureSecret(), "utf-8");
//            String realKey = keyPrefix + lowerCaseCode;

            //update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
            //redisUtil.removeAll(keyPrefix);
            String realKey=key;
            redisUtil.removeAll(key);
            //update-end---author:chenrui ---date:20250107  for：[QQYUN-10775]验证码可以复用 #7674------------
            redisUtil.set(realKey, lowerCaseCode, 60);
            System.out.println("captcha:" + lowerCaseCode);
            System.out.println("CheckKey:" + key);
            log.info("获取验证码，Redis key = {}，checkCode = {}", realKey, code);
            //返回前端
            String base64 = RandImageUtil.generate(code);
            res.setSuccess(true);
            res.setResult(base64);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            res.error500("获取验证码失败,请检查redis配置!");
            return res;
        }
        return res;
    }

    /**
     * 用户注册接口
     *
     * @param jsonObject
     * @param user
     * @return
     */
    @Operation(summary = "注册")
    @PostMapping("/register")
    @IgnoreAuth
    public Result<JSONObject> userRegister(@RequestBody JSONObject jsonObject ) {
        Result<JSONObject> result = new Result<JSONObject>();
        String phone = jsonObject.getString("phone");


        String username = jsonObject.getString("username");
        String nickname = jsonObject.getString("nickname");
        String password = jsonObject.getString("password1");
        String key = jsonObject.getString("key");
        String yzm1 = jsonObject.getString("yzm");
        Object code = redisUtil.get(key);
        if(code==null){
            result.setMessage("刷新验证码，重新输入");
            result.setSuccess(false);
            return result;
        }
        String yzm=code.toString().toLowerCase();
        if(!yzm.equals(yzm1)){
            result.setMessage("验证码不正确");
            result.setSuccess(false);
            return result;
        }




        if(oConvertUtils.isNotEmpty(username)){
            JkMemberInfo sysUser3 = sysUserService.getUserByName(username);
            if (sysUser3 != null) {
                result.setMessage("该账号已被注册");
                result.setSuccess(false);
                return result;
            }
        }

        String realname=nickname;

        try {
            JkMemberInfo user=new JkMemberInfo();
            user.setCreateTime(new Date());// 设置创建时间
            String salt = oConvertUtils.randomGen(8);
            String passwordEncode = PasswordUtil.encrypt(username, password, salt);
            user.setSalt(salt);
            user.setUsername(username);
            user.setRealname(realname);
            user.setPassword(passwordEncode);

            user.setEmail("");
            user.setPhone(phone);
            user.setMemberType(CommonConstantNumber.Common_Member);
            user.setIsEnabled(CommonConstantNumber.Enabled);
            sysUserService.addUser(user);//默认临时角色 test
            result.success("注册成功");
        } catch (Exception e) {
            result.error500("注册失败");
        }
        return result;
    }

    @AutoLog(value = "会员编辑个人信息")
    @Operation(summary="会员编辑")
    @RequestMapping(value = "/editme", method = {RequestMethod.PUT,RequestMethod.POST})
    public Result<String> edit(@RequestBody JkMemberInfo jkMemberInfo) {
        sysUserService.updateById(jkMemberInfo);
        return Result.OK("编辑成功!");
    }

}
